/**
 * Copyright(c) 2013 Jade Techonologies Co., Ltd.
 *
 * History:
 *   13-7-11 上午11:15 Created by sky
 */
package com.jade.modules.message.sms.comm;

import java.util.concurrent.atomic.AtomicBoolean;

import com.jade.framework.base.comm.*;
import com.jade.framework.base.comm.impl.SimpleProtocol;
import com.jade.framework.base.comm.netty.NettyClient;
import com.jade.modules.message.sms.*;
import org.apache.commons.lang3.StringUtils;

/**
 * 采用通用通信框架实现的客户端。
 *
 * @author sky
 * @version 1.0 13-7-11 上午11:15
 */
public class CommSmsClient
        extends AbstractSmsClient
        implements ClientProtocolHandler
{
    protected SmsConfiguration configuration;
    protected CommClient commClient;
    protected MessageCodec messageCodec;
    protected ProtocolAdapter protocolAdapter;
    protected AtomicBoolean inited = new AtomicBoolean(false);

    public void setConfiguration(SmsConfiguration configuration)
    {
        this.configuration = configuration;
    }

    public void setMessageCodec(MessageCodec messageCodec)
    {
        this.messageCodec = messageCodec;
    }

    public void setProtocolAdapter(ProtocolAdapter protocolAdapter)
    {
        this.protocolAdapter = protocolAdapter;
    }

    public void init()
    {
        NettyClient commClient = new NettyClient();
        SimpleProtocol protocol = new SimpleProtocol();
        protocol.setProtocolHandler(this);
        protocol.setConfiguration(configuration);
        protocol.setMessageCodec(messageCodec);
        protocol.setProtocolHandler(this);
        protocolAdapter.setConfiguration(configuration);
        commClient.setProtocol(protocol);
        commClient.init();
        this.commClient = commClient;
        inited.set(true);
    }

    @Override
    public SmsResult send(SmsMessage sms, SmsServiceParams params)
    {
        if (!inited.get()) {
            init();
        }

        if (StringUtils.isBlank(params.getServiceCode())) {
            params.setServiceCode(configuration.getServiceCode());
        }

        if (StringUtils.isBlank(params.getVasId())) {
            params.setVasId(configuration.getVasId());
        }

        if (StringUtils.isBlank(params.getVaspId())) {
            params.setVaspId(configuration.getVaspId());
        }
        if (!configuration.isSupportMultiReceiver()) {
            SmsResult result = new SmsResult(sms.getTo().length);
            String[] tos = sms.getTo();
            for (int i = 0; i < tos.length; i++) {
                sms.setTo(tos[i]);
                SmsResult r = doSend(sms, params);
                result.setResult(i, r.getMsgId(0), r.getCode(0), r.getMessage(0));
            }
            sms.setTo(tos);
            return result;
        }
        else {
            return doSend(sms, params);
        }
    }

    public SmsResult doSend(SmsMessage sms, SmsServiceParams params)
    {
        SmsRequest request = protocolAdapter.toSubmit(sms, params);
        try {
            SmsResponse response = (SmsResponse) commClient.sendRequest(request);
            return protocolAdapter.createResult(request, response);
        }
        catch (CommConnectException cce) {
            return new SmsResult(sms.getReceiverCount(), SmsStatus.SC_NETWORK_ERROR, cce.getMessage());
        }
        catch (CommTimeoutException cte) {
            return new SmsResult(sms.getReceiverCount(), SmsStatus.SC_HANDLE_TIMEOUT, cte.getMessage());
        }
        catch (BindException be) {
            return new SmsResult(sms.getReceiverCount(), SmsStatus.SC_AUTH_FAILED, be.getMessage());
        }
        catch (Throwable t) {
            return new SmsResult(sms.getReceiverCount(), SmsStatus.SC_TEMP_ERROR, t.getMessage());
        }
    }

    public void bind(ProtocolSession session)
            throws CommException, InterruptedException
    {
        SmsRequest bind = protocolAdapter.createBind();
        SmsResponse bindResp = (SmsResponse) session.getTransport().sendAndGetResponse(bind,
                configuration.getConnectTimeout());
        if (!bindResp.isOK()) {
            throw new BindException(bindResp.getStatus(), "bind error");
        }
    }

    public void connected(ProtocolSession session)
    {
        session.getLog().info("It's successful to connect schedule server!");
    }

    public void idle(ProtocolSession session)
    {
        try {
            SmsRequest ping = protocolAdapter.createPing();
            if (ping != null) {
                SmsResponse pingResp = (SmsResponse) session.getTransport().sendAndGetResponse(ping,
                        configuration.getConnectTimeout());
                if (pingResp.isOK()) {
                    session.getProtocol().getLog().debug("ping successful");
                }
                else {
                    session.getLog().error("ping failed:" + pingResp.getStatus());
                    session.getTransport().close();
                }
            }
        }
        catch (CommException e) {
            session.getLog().error("send ping message error! close connection", e);
            session.getTransport().close();
        }
        catch (InterruptedException ex) {
            session.getProtocol().getLog().error("send ping message error! close connection", ex);
            session.getTransport().close();
        }
    }

    public void disconnected(ProtocolSession session)
    {
        session.getLog().info("disconnect to server!");
    }

    public void received(ProtocolSession session, Object message)
    {
        SmsRequest smsRequest = (SmsRequest) message;
        SmsResponse smsResponse = null;
        if (smsRequest.isDataRequest()) {
            SmsServiceParams params = new SmsServiceParams();
            Object obj = protocolAdapter.toMessage(smsRequest, params);

            SmsResult result;
            //deliver
            if (obj instanceof SmsMessage) {
                result = smsListener.onMessage((SmsMessage) obj, params);
            }
            else {
                result = smsListener.onReport((SmsReport) obj, params);
            }

            smsResponse = protocolAdapter.createResponse(smsRequest, result);
        }
        else {
            SmsResponse response = protocolAdapter.handleNonDataRequest(smsRequest);
            if (response == null) {
                commClient.close();
            }
            smsResponse = response;
        }
        if (smsResponse != null) {
            try {
                session.getTransport().send(smsResponse);
            }
            catch (CommException e) {
                session.getLog().error("Can't send sms response for request: " + smsRequest.getCommandId(), e);
                session.getTransport().close();
            }
        }
    }

    public void caught(ProtocolSession session, Throwable t)
    {
        session.getLog().warn("Caugth an exception, connection will be closed", t);
        session.getTransport().close();
    }

//    public void bind(CommConnection connection)
//            throws CommException
//    {
//        SmsRequest bind = protocolAdapter.createBind();
//        SmsResponse bindResp = (SmsResponse) connection.sendRequestAndGetResponse(bind);
//        if (!bindResp.isOK()) {
//            throw new BindException(bindResp.getStatus(), "bind error");
//        }
//    }
//
//    public void ping(CommConnection connection)
//            throws CommException
//    {
//        SmsRequest ping = protocolAdapter.createPing();
//        if (ping != null) {
//            SmsResponse pingResp = (SmsResponse) connection.sendRequestAndGetResponse(ping);
//        }
//    }
//
//    public ResponseMessage handleRequest(RequestMessage request)
//    {
//        SmsRequest smsRequest = (SmsRequest) request;
//        if (smsRequest.isDataRequest()) {
//            SmsServiceParams params = new SmsServiceParams();
//            Object obj = protocolAdapter.toMessage(smsRequest, params);
//
//            SmsResult result;
//            //deliver
//            if (obj instanceof SmsMessage) {
//                result = smsListener.onMessage((SmsMessage) obj, params);
//            }
//            else {
//                result = smsListener.onReport((SmsReport) obj, params);
//            }
//
//            return protocolAdapter.createResponse(smsRequest, result);
//        }
//        else {
//            SmsResponse response = protocolAdapter.handleNonDataRequest(smsRequest);
//            if (response == null) {
//                commClient.close();
//            }
//            return response;
//        }
//    }

    @Override
    public void close()
    {
        if (commClient != null) {
            commClient.close();
        }
    }

    public boolean isReadable()
    {
        return configuration.getMode() == SmsConstants.MODE_READ ||
                configuration.getMode() == SmsConstants.MODE_READ_WRITE;
    }

    public boolean isWritable()
    {
        return configuration.getMode() == SmsConstants.MODE_WRITE ||
                configuration.getMode() == SmsConstants.MODE_READ_WRITE;
    }
}
